package top.scsoul.voes.framework.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import top.scsoul.voes.framework.filter.AuthFilter;
import top.scsoul.voes.framework.handle.ForbiddenAccessDeniedHandler;
import top.scsoul.voes.common.core.domain.entity.Permission;
import top.scsoul.voes.common.core.domain.entity.User;
import top.scsoul.voes.system.mapper.PermissionMapper;
import top.scsoul.voes.system.service.UserService;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

@Slf4j
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter implements UserDetailsService {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Resource
    UserService userService;
    @Resource
    PermissionMapper permissionMapper;

    @Resource
    AuthFilter authFilter;

    @Lazy
    @Resource
    PasswordEncoder passwordEncoder;

    @Resource
    ForbiddenAccessDeniedHandler forbiddenAccessDeniedHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(this.userDetailsService()).passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置权限
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry
                authorizeRequests
                = http.authorizeRequests();


        List<Permission> permissions = permissionMapper.queryAll(null);
        permissions.forEach(permission -> {
            authorizeRequests
                    .antMatchers(permission.getUrl())
                    .hasAuthority(permission.getPermissionKeyword());

        });
        authorizeRequests
                .antMatchers(
                        "/api/login",
                        "/api/captcha",
                        "/api/system/resetProgress",
                        "/api/**"
                )
                .permitAll();

        //配置前后端分离跨域等
        authorizeRequests
                .and()
                .headers()
                .frameOptions().disable().and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().accessDeniedHandler(forbiddenAccessDeniedHandler)
                .and()
                .cors()
                .and()
                .csrf()
                .disable();
        //配置过滤器
        authorizeRequests.and().addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> authorityList = new ArrayList<>();
        /* 此处查询数据库得到角色权限列表，这里可以用Redis缓存以增加查询速度 */
        User user = userService.queryByUsername(username);
        HashSet<Permission> permissions = new HashSet<>();
        user.getRoles().forEach(role -> {
            List<Permission> p = role.getPermissions();
            permissions.addAll(p);
        });
        permissions.forEach(permission -> {
            authorityList.add(new SimpleGrantedAuthority(permission.getPermissionKeyword()));
        });
        return new org.springframework.security.core.userdetails.User(username, user.getPassword(), authorityList);
    }
}
